﻿-- Author	: Kurapica

--------------------------
-- Marker
--------------------------
do
	IGAS:NewAddon("IGAS_Studio")

	-- Only one Marker will be created.
    local marker

	-- Marker
	local _WidgetName = "Marker"
	local _Base = "VirtualUIObject"

	-- Script
	local _Dir = {
		"TOP",
		"BOTTOM",
		"LEFT",
		"RIGHT",
		"TOPLEFT",
		"TOPRIGHT",
		"BOTTOMLEFT",
		"BOTTOMRIGHT",
	}

	--------------------------
	-- Fix Position
	--------------------------
	local _Range = 3

	local _FixPos = {
		TOP = CreateFrame("Frame", "IGAS_FIXPOS_TOP", coreF),
		BOTTOM = CreateFrame("Frame", "IGAS_FIXPOS_BOTTOM", coreF),
		LEFT = CreateFrame("Frame", "IGAS_FIXPOS_LEFT", coreF),
		RIGHT = CreateFrame("Frame", "IGAS_FIXPOS_RIGHT", coreF),
	}

	for dir, frm in pairs(_FixPos) do
		local txt = CreateFrame("Texture", "Texture", frm)

		frm.Visible = false
		frm.TopLevel = true
		frm.FrameStrata = "TOOLTIP"

		txt:SetAllPoints(frm)
		txt:SetTexture(1, 1, 1, 1)
	end

	_FixPos.TOP.Height = 2
	_FixPos.BOTTOM.Height = 2
	_FixPos.LEFT.Width = 2
	_FixPos.RIGHT.Width = 2

	local _PosTimer = CreateFrame("Timer", "IGAS_FIXPOS_TIMER", coreF)
	local _PosPool = {}
	local _PointsPool = {}

	local function FixPos()
		for dir, posFrm in pairs(_FixPos) do
			posFrm:ClearAllPoints()
			posFrm.Visible = false

			local method

			if dir == "TOP" then
				method = "GetTop"
			elseif dir == "BOTTOM" then
				method = "GetBottom"
			elseif dir == "LEFT" then
				method = "GetLeft"
			elseif dir == "RIGHT" then
				method = "GetRight"
			else
				break
			end

			local near
			local pos = marker.MarkedFrame[method](marker.MarkedFrame)
			
			if not pos then return end

			for _, frm in ipairs(_PosPool) do
				if frm[method](frm) and abs(frm[method](frm) - pos) <= _Range then
					if near then
						if dir == "TOP" or dir == "BOTTOM" then
							if frm:GetLeft() < near:GetLeft() then
								near = frm
							end
						else
							if frm:GetTop() > near:GetTop() then
								near = frm
							end
						end
					else
						near = frm
					end
				end
			end

			if near then
				if dir == "TOP" then
					posFrm:SetPoint("BOTTOM", near, "TOP")

					if marker.MarkedFrame:GetLeft() > near:GetLeft() then
						posFrm:SetPoint("LEFT", near, "LEFT", -16, 0)
					else
						posFrm:SetPoint("LEFT", marker.MarkedFrame, "LEFT", -16, 0)
					end

					if marker.MarkedFrame:GetRight() < near:GetRight() then
						posFrm:SetPoint("RIGHT", near, "RIGHT", 16, 0)
					else
						posFrm:SetPoint("RIGHT", marker.MarkedFrame, "RIGHT", 16, 0)
					end
				elseif dir == "BOTTOM" then
					posFrm:SetPoint("TOP", near, "BOTTOM")

					if marker.MarkedFrame:GetLeft() > near:GetLeft() then
						posFrm:SetPoint("LEFT", near, "LEFT", -16, 0)
					else
						posFrm:SetPoint("LEFT", marker.MarkedFrame, "LEFT", -16, 0)
					end

					if marker.MarkedFrame:GetRight() < near:GetRight() then
						posFrm:SetPoint("RIGHT", near, "RIGHT", 16, 0)
					else
						posFrm:SetPoint("RIGHT", marker.MarkedFrame, "RIGHT", 16, 0)
					end
				elseif dir == "LEFT" then
					posFrm:SetPoint("RIGHT", near, "LEFT")

					if marker.MarkedFrame:GetTop() < near:GetTop() then
						posFrm:SetPoint("TOP", near, "TOP", 0, 16)
					else
						posFrm:SetPoint("TOP", marker.MarkedFrame, "TOP", 0, 16)
					end

					if marker.MarkedFrame:GetBottom() > near:GetBottom() then
						posFrm:SetPoint("BOTTOM", near, "BOTTOM", 0, -16)
					else
						posFrm:SetPoint("BOTTOM", marker.MarkedFrame, "BOTTOM", 0, -16)
					end
				elseif dir == "RIGHT" then
					posFrm:SetPoint("LEFT", near, "RIGHT")

					if marker.MarkedFrame:GetTop() < near:GetTop() then
						posFrm:SetPoint("TOP", near, "TOP", 0, 16)
					else
						posFrm:SetPoint("TOP", marker.MarkedFrame, "TOP", 0, 16)
					end

					if marker.MarkedFrame:GetBottom() > near:GetBottom() then
						posFrm:SetPoint("BOTTOM", near, "BOTTOM", 0, -16)
					else
						posFrm:SetPoint("BOTTOM", marker.MarkedFrame, "BOTTOM", 0, -16)
					end
				end

				posFrm.Near = near
				posFrm.Visible = true
			end
		end
	end

	function _PosTimer:OnTimer()
		FixPos()
	end

	local function StartFixPos()
		if marker and marker.MarkedFrame and not marker.MarkedFrame:IsObjectType("VirtualUIObject") then
			local parent = _FrameInfoList[marker.MarkedFrame].Parent

			for frm, info in pairs(_FrameInfoList) do
				if frm ~= marker.MarkedFrame and info.Parent == parent and not frm:IsObjectType("VirtualUIObject") then
					tinsert(_PosPool, frm)
				end
			end

			_PosTimer.Interval = 0.1
		end
	end

	local function GetPos(frame, point, top, bottom, left, right)
		local x, y = frame:GetCenter()

		if strfind(point, "TOP") then
			y = top or frame:GetTop()
		elseif strfind(point, "BOTTOM") then
			y = bottom or frame:GetBottom()
		end

		if strfind(point, "LEFT") then
			x = left or frame:GetLeft()
		elseif strfind(point, "RIGHT") then
			x = right or frame:GetRight()
		end

		return x, y
	end

	local function StopFixPos()
		_PosTimer.Interval = 0

		FixPos()
		wipe(_PosPool)

		local mark = marker.MarkedFrame

		mark:StopMovingOrSizing()
		mark:ClearAllPoints()

		--[[ Fix Size
		if _FixPos["TOP"].Visible and _FixPos["BOTTOM"].Visible then
			mark.Height = _FixPos["TOP"].Near:GetTop() - _FixPos["BOTTOM"].Near:GetBottom()
		end

		if _FixPos["LEFT"].Visible and _FixPos["RIGHT"].Visible then
			mark.Width = _FixPos["RIGHT"].Near:GetRight() - _FixPos["LEFT"].Near:GetLeft()
		end--]]

		-- Get Pos
		local top, bottom, left, right
		
		top = mark:GetTop()
		bottom = mark:GetBottom()
		left = mark:GetLeft()
		right = mark:GetRight()

		if _FixPos["TOP"].Visible then
			top = _FixPos["TOP"].Near:GetTop()
		end

		if _FixPos["BOTTOM"].Visible then
			bottom = _FixPos["BOTTOM"].Near:GetBottom()
		end

		if _FixPos["LEFT"].Visible then
			left = _FixPos["LEFT"].Near:GetLeft()
		end

		if _FixPos["RIGHT"].Visible then
			right = _FixPos["RIGHT"].Near:GetRight()
		end

		for dir, posFrm in pairs(_FixPos) do
			posFrm.Visible = false
			posFrm.Near = nil
			posFrm:ClearAllPoints()
		end
		
		for point, detail in pairs(_PointsPool) do
			local x, y = GetPos(mark, point, top, bottom, left, right)
			local rx, ry = GetPos(detail.relativeTo, detail.relativePoint)

			mark:SetPoint(point, detail.relativeTo, detail.relativePoint, x - rx, y - ry)
		end
	end

	-- Dirs's Scripts
	local function DirOnMouseDown(self)
		if marker.InMarkMode and marker.MarkedFrame and not marker.MarkedFrame.IGAS_STUDIO_MARKER.IsContainer then
			wipe(_PointsPool)

			for i = 1, marker.MarkedFrame:GetNumPoints() do
				local point, relativeTo, relativePoint, xOffset, yOffset = marker.MarkedFrame:GetPoint(i)
				_PointsPool[point] = {	point = point,
										relativeTo = relativeTo,
										relativePoint = relativePoint,
										xOffset = xOffset,
										yOffset = yOffset,}
			end

			marker.MarkedFrame:StartSizing(self.__Dir)
			marker.IsStartResize = true
			StartFixPos()
		end
	end

	local function DirOnMouseUp(self)
		if marker.IsStartResize then
			marker.IsStartResize = false
			StopFixPos()
		end
	end

	-- Hook Frame Scripts
	local _MoveTimer = CreateFrame("Timer", "IGAS_MarkerMove_TIMER", coreF)

	function _MoveTimer:OnTimer()
		_MoveTimer.Interval = 0

		wipe(_PointsPool)

		for i = 1, marker.MarkedFrame:GetNumPoints() do
			local point, relativeTo, relativePoint, xOffset, yOffset = marker.MarkedFrame:GetPoint(i)
			_PointsPool[point] = {	point = point,
									relativeTo = relativeTo,
									relativePoint = relativePoint,
									xOffset = xOffset,
									yOffset = yOffset,}
		end

		marker.MarkedFrame:StartMoving()
		marker.IsStartMoving = true
		StartFixPos()
	end

	local function frameOnMouseUp(self, ...)
		_MoveTimer.Interval = 0

		if marker.IsStartMoving then
			marker.IsStartMoving = false
			StopFixPos()
		end
	end

	local function frameOnMouseDown(self, ...)
		marker.MarkedFrame = self

		if marker.InMarkMode and not self.IGAS_STUDIO_MARKER.IsContainer then
			_MoveTimer.Interval = 0.2
		end
	end

	local function frameOnEnter(self)
		if marker.InMarkMode and self ~= marker.MarkedFrame then
			self.IGAS_STUDIO_MARKER.MaskColor.Visible = true
		end
	end

	local function frameOnLeave(self)
		if marker.InMarkMode and self ~= marker.MarkedFrame then
			self.IGAS_STUDIO_MARKER.MaskColor.Visible = false
		end
	end

	-- ScriptType
	local _ScriptType = {
	}

	-- Function
	local _Function = {
		-- HookFrame
		["HookFrame"] = function(self, frame, isContainer)
			local mask = CreateFrame("Frame", "IGAS_STUDIO_MARKER", frame)

			mask:SetAllPoints(frame)
			mask.Visible = true
			mask.TopLevel = true
			mask.IsContainer = (isContainer and true) or false

			local t = CreateFrame("Texture", "MaskColor", mask)
			t:SetTexture(0.3, 0.1, 0.4, 0.5)
			t:SetAllPoints(mask)
			t.Visible = false

			for _, d in ipairs(_Dir) do
				local dir = CreateFrame("Frame", "Marker_"..d, mask)
				dir.__Dir = d
				dir.Width = 12
				dir.Height = 12
				dir.TopLevel = true
				dir.Visible = false
				dir.MouseEnabled = true
				dir.OnMouseUp = DirOnMouseUp
				dir.OnMouseDown = DirOnMouseDown

				t = CreateFrame("Texture", "Back", dir, "BACKGROUND")
				t:SetTexture("Interface\\Buttons\\UI-CheckBox-Up.blp")
				t:SetAllPoints(dir)
				dir:SetPoint("CENTER", mask, d, 0, 0)
			end

			frame.__UI:EnableMouse(true)
			frame.__UI:SetMovable(true)
			frame.__UI:SetResizable(true)

            frame:StackScript("OnMouseUp", frameOnMouseUp)
			frame:StackScript("OnMouseDown", frameOnMouseDown)
			frame:StackScript("OnEnter", frameOnEnter)
			frame:StackScript("OnLeave", frameOnLeave)
		end,
	}

	-- Property
	local _Property = {
		-- InMarkMode
		["InMarkMode"] = {
			["Set"] = function(self, flag)
				self.__InMarkMode = (flag and true) or false
				if not self.__InMarkMode and self.__MarkedFrame then
					if not self.__MarkedFrame:IsObjectType("VirtualUIObject") then
						self.__MarkedFrame.IGAS_STUDIO_MARKER.MaskColor.Visible = false
						for _, d in ipairs(_Dir) do
							self.__MarkedFrame.IGAS_STUDIO_MARKER["Marker_"..d].Visible = false
						end
					end
					self.__MarkedFrame = nil
					_Addon:FireEvent("IGAS_STUDIO_FRAME_SELECTED", nil)
				end
			end,

			["Get"] = function(self)
				return (self.__InMarkMode and true) or false
			end,

			["Type"] = IGAS:GetPropertyType("Boolean"),
		},
		-- MarkedFrame
		["MarkedFrame"] = {
			["Set"] = function(self, frame)
				if not self.__InMarkMode then
					return
				end
				if frame and type(frame) == "table" and frame.IsIGASFrame then
					if self.__MarkedFrame ~= frame then
						if self.__MarkedFrame and not self.__MarkedFrame:IsObjectType("VirtualUIObject") then
							self.__MarkedFrame.IGAS_STUDIO_MARKER.MaskColor.Visible = false
							for _, d in ipairs(_Dir) do
								self.__MarkedFrame.IGAS_STUDIO_MARKER["Marker_"..d].Visible = false
							end
						end
						self.__MarkedFrame = frame
						if not frame:IsObjectType("VirtualUIObject") then
							self.__MarkedFrame.IGAS_STUDIO_MARKER.MaskColor.Visible = false
							for _, d in ipairs(_Dir) do
								self.__MarkedFrame.IGAS_STUDIO_MARKER["Marker_"..d].Visible = true
							end
						end
						_Addon:FireEvent("IGAS_STUDIO_FRAME_SELECTED", frame)
					end
				elseif self.__MarkedFrame then
					if not self.__MarkedFrame:IsObjectType("VirtualUIObject") then
						self.__MarkedFrame.IGAS_STUDIO_MARKER.MaskColor.Visible = false
						for _, d in ipairs(_Dir) do
							self.__MarkedFrame.IGAS_STUDIO_MARKER["Marker_"..d].Visible = false
						end
					end
					self.__MarkedFrame = nil
					_Addon:FireEvent("IGAS_STUDIO_FRAME_SELECTED", nil)
				end
			end,

			["Get"] = function(self)
				return self.__MarkedFrame
			end,

			["Type"] = IGAS:GetPropertyType("Object"),
		},
	}

	local function _New(name, parent)
		if marker then
			return marker
		end

		marker = CreateFrame(_Base, name, parent)

		marker.__InMarkMode = true

		return marker
	end

	-- Register
	local _WidgetInfo = {
		["WidgetName"] = _WidgetName,
		["Base"] = _Base,
		["Function"] = _Function,
		["Property"] = _Property,
		["ScriptType"] = _ScriptType,
		["New"] = _New,
	}

	Widget[_WidgetName] = IGAS:NewWidget(_WidgetInfo)
end

